package com.mybatis.jpa.meta;

import com.mybatis.jpa.annotation.*;
import com.mybatis.jpa.common.PersistentUtil;
import org.apache.ibatis.builder.BuilderException;
import org.apache.ibatis.type.*;

import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Lob;
import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Set;

/**
 * mybatis column元数据类型
 *
 * @author svili
 * @data 2017年5月6日
 */
public class MybatisColumnMeta {

    /**
     * 是否为主键
     */
    private boolean primaryKey;

    /**
     * Java fieldName
     */
    private String property;

    private String columnName;

    /**
     * fieldType
     */
    private Class<?> type;

    /**
     * mybatis jdbcTypeAlias
     */
    private String jdbcTypeAlias;

    /**
     * mybatis jdbcType
     */
    private JdbcType jdbcType;

    /**
     * 是否是like字段
     */
    private boolean isLike;

    /**
     * 左边like
     */
    private boolean isLLike;

    /**
     * 右边like
     */
    private boolean isRLike;

    /**
     * 两者之间
     */
    private boolean isBetween;

    /**
     * 是不是in
     */
    private boolean isIn;

    /**
     * 是不是!=
     */
    private boolean nEQ;

    /**
     * 是不是数据权限过滤字段
     */
    private boolean isPermissonDataField;

    /**
     * 乐观锁插件
     */
    private boolean isVersion;


    /**
     * 类信息
     */
    private PersistentMeta persistentMeta;

    private static Set<String> keyWordSet = new HashSet<>();

    static {

    }

    /**
     * mybatis typeHandler
     */
    private Class<? extends TypeHandler<?>> typeHandlerClass;

    /**
     * 持久化字段
     */
    private Field field;

    public MybatisColumnMeta(Field field, PersistentMeta persistentMeta) {
        this.field = field;
        // 初始化
        this.property = field.getName();
        this.columnName = PersistentUtil.getColumnName(field);
        this.type = field.getType();
        this.jdbcTypeAlias = ColumnMetaResolver.resolveJdbcAlias(field);
        this.jdbcType = ColumnMetaResolver.resolveJdbcType(this.jdbcTypeAlias);
        this.typeHandlerClass = ColumnMetaResolver.resolveTypeHandler(field);
        this.persistentMeta = persistentMeta;
        this.isLike = field.isAnnotationPresent(Like.class);
        this.isLLike = field.isAnnotationPresent(LLike.class);
        this.isRLike = field.isAnnotationPresent(RLike.class);
        this.isBetween = field.isAnnotationPresent(Between.class);
        this.isIn = field.isAnnotationPresent(IN.class);
        this.nEQ = field.isAnnotationPresent(NEQ.class);
        this.isPermissonDataField = field.isAnnotationPresent(DataPermisson.class);
        this.isVersion = field.isAnnotationPresent(javax.persistence.Version.class) || field.isAnnotationPresent(com.baomidou.mybatisplus.annotation.Version.class);

    }

    /**
     * meta resolver
     */
    private static class ColumnMetaResolver {

        public static String resolveJdbcAlias(Field field) {

            Class<?> fieldType = field.getType();
            if (field.getType().isEnum()) {
                if (field.isAnnotationPresent(Enumerated.class)) {
                    // 获取注解对象
                    Enumerated enumerated = field.getAnnotation(Enumerated.class);
                    // 设置了value属性
                    if (enumerated.value() == EnumType.ORDINAL) {
                        return "INTEGER";
                    }
                }
                return "VARCHAR";
            }
            if (field.isAnnotationPresent(Lob.class)) {
                if (String.class.equals(fieldType)) {
                    return "CLOB";
                }
            }
            if (Integer.class.equals(fieldType)) {
                return "INTEGER";
            }
            if (Double.class.equals(fieldType)) {
                return "DOUBLE";
            }
            if (Float.class.equals(fieldType)) {
                return "FLOAT";
            }
            if (Long.class.equals(fieldType)) {
                return "BIGINT";
            }
            if (String.class.equals(fieldType)) {
                return "VARCHAR";
            }
            // date类型需声明
            if (java.util.Date.class.isAssignableFrom(fieldType)) {
                return "TIMESTAMP";
            }
            return null;
        }

        public static JdbcType resolveJdbcType(String alias) {
            if (alias == null) {
                return null;
            }
            try {
                return JdbcType.valueOf(alias);
            } catch (IllegalArgumentException e) {
                throw new BuilderException("Error resolving JdbcType. Cause: " + e, e);
            }
        }

        @SuppressWarnings("unchecked")
        public static Class<? extends TypeHandler<?>> resolveTypeHandler(Field field) {

            if (field.getType().isEnum()) {
                if (field.isAnnotationPresent(Enumerated.class)) {
                    // 获取注解对象
                    Enumerated enumerated = field.getAnnotation(Enumerated.class);
                    // 设置了value属性
                    if (enumerated.value() == EnumType.ORDINAL) {
                        return (Class) EnumOrdinalTypeHandler.class;
                    }
                }
                return (Class) EnumTypeHandler.class;
            }

            Class<? extends TypeHandler<?>> typeHandlerClass = null;
            if (field.getType().equals(Boolean.class)) {
                typeHandlerClass = (Class) BooleanTypeHandler.class;
            }
            return typeHandlerClass;
        }
    }

    // getter
    public boolean isPrimaryKey() {
        return primaryKey;
    }

    public String getProperty() {
        return property;
    }

    public String getColumnName() {
        if (keyWordSet.contains(columnName)) {
            return "`" + columnName + "`";
        }
        return columnName;
    }

    public Class<?> getType() {
        return type;
    }

    public String getJdbcTypeAlias() {
        return jdbcTypeAlias;
    }

    public JdbcType getJdbcType() {
        return jdbcType;
    }

    public Class<? extends TypeHandler<?>> getTypeHandlerClass() {
        return typeHandlerClass;
    }

    public Field getField() {
        return field;
    }

    public boolean isLike() {
        return isLike;
    }

    public void setLike(boolean isLike) {
        this.isLike = isLike;
    }

    public boolean isLLike() {
        return isLLike;
    }

    public void setLLike(boolean isLLike) {
        this.isLLike = isLLike;
    }

    public boolean isRLike() {
        return isRLike;
    }

    public void setRLike(boolean isRLike) {
        this.isRLike = isRLike;
    }

    public boolean isBetween() {
        return isBetween;
    }

    public boolean isIn() {
        return isIn;
    }

    public void setIn(boolean in) {
        isIn = in;
    }

    public boolean isnEQ() {
        return nEQ;
    }

    public void setnEQ(boolean nEQ) {
        this.nEQ = nEQ;
    }

    public void setBetween(boolean isBetween) {
        this.isBetween = isBetween;
    }

    public boolean isPermissonDataField() {
        return isPermissonDataField;
    }

    public void setPermissonDataField(boolean permissonDataField) {
        isPermissonDataField = permissonDataField;
    }

    public boolean isVersion() {
        return isVersion;
    }

    public void setVersion(boolean version) {
        isVersion = version;
    }
}
